﻿
// CompileDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "Compile.h"
#include "CompileDlg.h"
#include "afxdialogex.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#include <iostream>
#include <vector>
#include <sstream> 
#include <stack>
using namespace std;


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

	// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CCompileDlg 对话框

CCompileDlg::CCompileDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_COMPILE_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CCompileDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	//  DDX_Text(pDX, IDC_EDIT_Input, INPUT);
	DDX_Control(pDX, IDC_EDIT_Input, InputControl);
	DDX_Control(pDX, IDC_EDIT_After, After);
	//DDX_Control(pDX, IDC_EDIT_Result, Result);
	DDX_Control(pDX, IDC_EDIT_Fault, Fault);
}

BEGIN_MESSAGE_MAP(CCompileDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_Clear, &CCompileDlg::OnClickedButtonClear)
	ON_BN_CLICKED(IDC_BUTTON_Translate, &CCompileDlg::OnClickedButtonTranslate)
END_MESSAGE_MAP()


// CCompileDlg 消息处理程序

BOOL CCompileDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时，框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE
}

void CCompileDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CCompileDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CCompileDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

/*************************************************************************/
// 检测是否是空白符
bool IsBlank(char ch)
{
	return ch == ' ' || ch == '\t';
}

// 检测Text是否是Stream从pos起始的开头
// 如果是，则将pos前移Text.size()个字符，且返回true
// 否则pos保持不变，返回false
// 对于pos后的空白符忽略
bool IsPrefix(const string& Stream, int& pos, const string& Text)
{
	int read = pos; // 设置真实游标

	// 过滤空格
	while (IsBlank(Stream[read]))
	{
		++read;
	}
	if (Stream.substr(read, Text.size()) == Text)
	{
		pos = read + Text.size();
		return true;
	}
	else
		return false;
}

// 检测Stream从pos开头是否是数字
void CCompileDlg::GetNumber(const string& Stream, int& pos)
{
	bool GotNumber = false;

	int read = pos;

	// 过滤空格
	while (IsBlank(Stream[read]))
	{
		++read;
	}

	if (read >= 1 && (Stream[read] == '-' || Stream[read] == '+') && Stream[read - 1] == '(')
		++read;
	else if (read == 0 && (Stream[read] == '-' || Stream[read] == '+'))
		++read;

	while (Stream[read] >= '0' && Stream[read] <= '9')
	{
		GotNumber = true;
		++read;
	}

	if (GotNumber)
		pos = read;
	else
	{
		//不是数字
		throw Exception(read, "此处需要表达式");
	}
}

void GetTerm(const string& Stream, int& pos);
void GetFactor(const string& Stream, int& pos);
void GetExp(const string& Stream, int& pos);

// 检测Stream从pos起始是否是一个Term
// 实现语法1：Term = <数字> | "("Exp")"
void CCompileDlg::GetTerm(const string& Stream, int& pos)
{
	try
	{
		GetNumber(Stream, pos);
	}
	catch (Exception& e)
	{
		int read = pos;
		// 检测左括号
		if (IsPrefix(Stream, read, "("))
		{
			// 检测表达式
			GetExp(Stream, read);
			if (IsPrefix(Stream, read, ")"))
				// 如果使用右括号结束，则返回结果
				pos = read;
			else // 否则抛出异常
			{
				Judge_flag = false;
				throw Exception(read, "此处需要右括号");
			}
		}
		else
		{
			Judge_flag = false;
			throw Exception(read, "此处需要数字或左括号");
		}
	}
}

// 检测Stream从pos起始，开头是否是Factor
// 实现语法2：Factor = Term (("*" | "/") Term)*
void CCompileDlg::GetFactor(const string& Stream, int& pos)
{
	int read = pos;
	GetTerm(Stream, read); //执行Term

	//判断是否为+或-
	while (IsPrefix(Stream, read, "*") || IsPrefix(Stream, read, "/"))
	{
		// 如果是乘除号，则获得下一个Term
		try
		{
			GetTerm(Stream, read);
		}
		catch (Exception& e)
		{
			Judge_flag = false;
			throw e;
		}

	}
	pos = read;
}

// 检测Stream从pos起始，开头是否是一个Exp
// 实现语法3：Exp = Factor (("+" | "-") Factor)*
void CCompileDlg::GetExp(const string& Stream, int& pos)
{
	int read = pos;
	GetFactor(Stream, read);

	while (IsPrefix(Stream, read, "+") || IsPrefix(Stream, read, "-"))
	{
		// 如果是加减号，则获得下一个Factor
		try
		{
			GetFactor(Stream, read);
		}
		catch (Exception& e)
		{
			Judge_flag = false;
			throw e;
		}

	}
	pos = read;
}

//运算符优先级及判断是否为数字
int CCompileDlg::getPriority(string ch)
{
	if (ch == "+" || ch == "-")
		return 1;
	else if (ch == "*" || ch == "/")
		return 2;
	else if (ch == "(")
		return 3;
	else if (ch == ")")
		return -1;
	else
		return 0; //数字

}
//string转化为int
int string_to_int(string a)
{
	stringstream ss;
	int s;
	ss << a;
	ss >> s;
	return s;
}
//多位数的中缀表达式转换
vector<string> CCompileDlg::num_to_string(string str)
{
	vector<string>a;
	int j, i;
	i = 0;
	while (i < str.length())
	{
		j = i;
		while (j < str.length() && str[j] <= '9' && str[j] >= '0') { //计算多位数字
			j++;
		}
		if (i == j) {  //运算符 或 其他字符
			a.push_back(str.substr(i, 1));
			i = i + 1;
			string_num.push_back(1);
		}
		else { //多位数字
			if (i >= 2 && (str[i - 1] == '-' || str[i - 1] == '+') && str[i - 2] == '(')
			{
				i = i - 1;
				a.pop_back();
			}
			a.push_back(str.substr(i, j - i));
			string_num.push_back(j - i);
			i = j;
		}
	}
	return a;
}
//寻找错误位置
int CCompileDlg::Location(int i)
{
	int location = 0;
	for (int k = 0; k < i; k++) {
		location += string_num.at(k);
	}

	return location;
}
void CCompileDlg::OutputFault(int i, vector<string> str, string op)
{
	int k = i;

	for (; k >= 0; k--)
	{
		if (str[k] == op)
			break;
	}

	Judge_Temp = Location(k) + 1;
	Judge_flag = false;
	res.clear();
}
//中缀表达式转化为后缀表达式
vector<string> CCompileDlg::GetPostfixExpression(vector<string> str)
{
	stack<string> OpStack; //运算符栈
	int length = str.size(); //中缀表达式的长度
	int i = 0;

	while (i < length)
	{
		if (getPriority(str[i]) == 0) //如果为数字
		{
			res.push_back(str[i]); //后缀表达式栈
			result.push(string_to_int(str[i])); //计算结果栈
		}

		else //若为运算符
		{
			if (getPriority(str[i]) == 1 || getPriority(str[i]) == 2) //如果为+、-、*、/
			{
				if (OpStack.empty())  //栈空入栈
					OpStack.push(str[i]);
				else
				{
					if (getPriority(str[i]) > getPriority(OpStack.top())) //入栈操作符优先级比栈顶操作符优先级高，则入栈
					{
						OpStack.push(str[i]);
					}
					else {  //弹栈并输出，直到栈为空或遇到优先级更低的操作符 (除了左括号)
						while ((!OpStack.empty()) && getPriority(OpStack.top()) >= getPriority(str[i]) && OpStack.top() != "(")
						{
							res.push_back(OpStack.top()); //进入res栈
							Calculate(OpStack.top()); //计算
							//计算错误（若分母为0）
							if (!Judge_flag && OpStack.top() == "/") {
								OutputFault(i - 1, str, OpStack.top());//错误位置
								fault = "分母不能为0！";
								return res;
							}

							OpStack.pop(); //从运算符栈中弹出
						}
						OpStack.push(str[i]); //将操作符入栈
					}
				}
			}
			else if (getPriority(str[i]) == 3) //如果为(
			{
				OpStack.push(str[i]);
			}
			else if (getPriority(str[i]) == -1) //如果为)
			{
				string temp = OpStack.top();
				while (getPriority(temp) != 3) //如果不为(，弹栈并输出，直到栈顶为(
				{
					OpStack.pop();
					res.push_back(temp);

					Calculate(temp); //计算
					//计算错误（若分母为0）
					if (!Judge_flag && temp == "/") {
						OutputFault(i - 1, str, temp);//错误位置
						fault = "分母不能为0！";
						return res;
					}
					temp = OpStack.top();
				}
				OpStack.pop(); //弹出(
			}
		}
		i++;
	}

	//剩余栈内元素入res
	while (!OpStack.empty())
	{
		res.push_back(OpStack.top());

		Calculate(OpStack.top());
		//计算错误（若分母为0）
		if (!Judge_flag && OpStack.top() == "/") {
			OutputFault(i - 1, str, OpStack.top());//错误位置
			fault = "分母不能为0！";
			return res;
		}
		OpStack.pop();
	}

	return res;
}

//计算结果
double CCompileDlg::Calculate(string s)
{
	double num_a, num_b;

	num_a = result.top();
	result.pop();

	if (result.empty()) {
		num_b = 0;
	}
	else {
		num_b = result.top();
		result.pop();
	}

	//运算
	if (s == "+")
		result.push(num_b + num_a);

	else if (s == "-")
		result.push(num_b - num_a);

	else if (s == "*")
		result.push(num_a * num_b);

	else if (s == "/") {
		if (num_a == 0) {
			Judge_flag = false;
			return -1;
		}
		result.push(num_b / num_a);
	}

	return result.top();
}

//清空输入内容
void CCompileDlg::OnClickedButtonClear()
{
	InputControl.SetWindowText(_T(""));
}

//翻译为后缀表达式
void CCompileDlg::OnClickedButtonTranslate()
{
	//清空所有信息
	After.SetWindowText(_T(""));
	Fault.SetWindowText(_T(""));
	//清空所有栈，所有数据归零初始化
	res.clear(); //清空栈
	while (!result.empty()) result.pop();  //清空结果栈
	string_num.clear();
	Judge_Temp = 0;
	Judge_flag = true;


	string Mid_Str; //中缀表达式
	CString str;
	GetDlgItem(IDC_EDIT_Input)->GetWindowText(str);//获取输入的中缀表达式
	Mid_Str = (CT2A)str; //CString转为string

	int pos = 0;  //位置
	try
	{
		GetExp(Mid_Str, pos);

		while (pos < Mid_Str.size() && IsBlank(Mid_Str[pos]))
		{
			++pos;
		}
		if (pos < Mid_Str.size())
		{
			Judge_flag = false;
			throw Exception(pos, "发现多余的字符");
		}
	}
	catch (Exception& e)
	{
		CString int_to_string;
		//输出具体位置
		CString str = _T("错误所在的位置是：");
		int_to_string.Format(_T("%d"), e.Loc); //转换为CString
		str += _T(" ") + int_to_string + _T("\r\n");
		//输出错误原因
		str += _T("错误原因：");
		str += e.Error.c_str();
		Fault.SetWindowText(str);
	}

	if (Judge_flag) {
		//将算术表达式转换为<vector>string 解决多位数字问题
		vector<string> Mid_Str_Vector;
		Mid_Str_Vector = num_to_string(Mid_Str);

		//转换为后缀表达式
		vector <string> res_vector = GetPostfixExpression(Mid_Str_Vector); //vector <string>类型的后缀表达式

		if (Judge_flag) {
			string res_str = "";

			//vector <string>类型转换为string
			for (int i = 0; i < res_vector.size(); i++) {
				res_str += res_vector.at(i);
			}
			CString Aft_str;
			Aft_str = res_str.c_str();  //string转为CString
			//输出后缀表达式
			After.SetWindowText(Aft_str);
			//输出结果
			double result_double = CCompileDlg::result.top(); //double类型的结果
			CString result_CString;
			result_CString.Format(_T("%lf"), result_double); //double转换为CString
			Fault.SetWindowText(result_CString); //输出到edti中

		}
		else {
			CString int_to_string;
			//输出具体位置
			CString str = _T("错误所在的位置是：");
			int_to_string.Format(_T("%d"), Judge_Temp); //转换为CString
			str += _T(" ") + int_to_string + _T("\r\n");
			//输出错误原因
			str += _T("错误原因：");
			str += fault.c_str();
			Fault.SetWindowText(str);
		}
	}
}


